﻿/*
 Copyright aswing.org, see the LICENCE.txt.
*/

import org.aswing.awml.AwmlParser;
import org.aswing.awml.AwmlUtils;
import org.aswing.EventDispatcher;
import org.aswing.util.Delegate;
import org.aswing.util.EnterFrameImpulser;
import org.aswing.util.Impulser;
import org.aswing.util.Timer;

/**
 * Provides load handling routines for AWML files. 
 * 
 * @author Igor Sadovskiy
 */
class org.aswing.awml.AwmlLoader extends EventDispatcher {

    /**
     * When the loading is started.
     *<br>
     * onLoadStart(loader:AwmlLoader)
     */ 
    public static var ON_LOAD_START:String = "onLoadStart"; 

    /**
     * When the loading is completed.
     *<br>
     * onLoadComplete(loader:AwmlLoader, awml:XMLNode)
     */ 
    public static var ON_LOAD_COMPLETE:String = "onLoadComplete";   

    /**
     * When the initializing (parsing) is completed.
     *<br>
     * onInitComplete(loader:AwmlLoader)
     */ 
    public static var ON_INIT_COMPLETE:String = "onInitComplete";   

    /**
     * When the loading is failed.
     *<br>
     * onLoadFail(loader:AwmlLoader)
     */ 
    public static var ON_LOAD_FAIL:String = "onLoadFail";   

	/**
	 * Fires load progress event in period specified with #setLoadProgressInterval method.
	 *<br>
	 * onLoadProgress(source:AwmlLoader, loadedBytes:Number, totalBytes:Number)
	 */	
	public static var ON_LOAD_PROGRESS:String = "onLoadProgress";

	/**
	 * Default interval between firing #ON_PROGRESS_LOAD events. Equals to 1 second.
	 */
	public static var LOAD_PROGRESS_INTERVAL:Number = 1000;
    
    /** 
     * <code>LoadVars</code> instance for loading the content. 
     */
    private var loader:LoadVars;
    
    /** 
     * Specifies if AWML needs to be initialized (parsed) on load ot not
     */
    private var initOnLoad:Boolean;
    
    /**
     * #ON_INIT_COMPLETE event dispatcher wrapper to postpone event 
     * dispatching to the next frame.    
     */
    private var suspendedInitializer:Impulser;
        
    /**
     * Fires #ON_LOAD_PROGRESS event in specified interval.
     */
    private var loadProgressTracker:Impulser;
    
    /** 
     * AwmlLoader()<br>
     * AwmlLoader(initOnLoad)<br>
     * 
     * <p>Constructors new <code>AwmlLoader</code> instance. If <code>initOnLoad</code>
     * argument is <code>true</code> AWML will be initialized (parsed) after loading
     * automatically.
     * 
     * <p>Also if <code>initOnLoad</code> is <code>true</code> action listeners will be
     * invoked after AWML will be initialized (parsed). Else action listeners will 
     * be ivnoved once AWML will be loaded.    
     * 
     * @param initOnLoad (optional) the flag specified if AWML needs to be
     * initialized (parsed) on load or not. Default is <code>true</code> 
     */
    public function AwmlLoader(initOnLoad:Boolean) {
        super();
        
        loadProgressTracker = new Timer(LOAD_PROGRESS_INTERVAL, true);
        loadProgressTracker.addActionListener(fireLoadProgressEvent, this);
        
        loader = new LoadVars();
        loader.onData = Delegate.create(this, __onData);
        
        this.initOnLoad = (initOnLoad != null) ? initOnLoad : true;
        suspendedInitializer = new EnterFrameImpulser(1, false);
        suspendedInitializer.addActionListener(__onInit, this);
    }
    
    /**
     * addActionListener(fuc:Function, obj:Object)<br>
     * addActionListener(fuc:Function)<br>
     * Adds a action listener to the loader. Loader fires a action event when 
     * AWML file is loaded.
     * @param fuc the listener function.
     * @param obj which context to run in by the func.
     * @return the listener just added.
     * @see EventDispatcher#ON_ACT
     */
    public function addActionListener(fuc:Function, obj:Object):Object{
        return addEventListener(ON_ACT, fuc, obj);
    }
    
    /**
     * Start loading AWML file from the specified <code>url</code>.
     * 
     * @param url the URL there AWML file is located. 
     */
    public function load(url:String):Void {
        if (loader.load(url)) {
            fireLoadStartEvent();
            loadProgressTracker.start();   
        } else {
            fireLoadFailEvent();    
        }
    } 
    
    /**
     * Checks if AWML file loading is currently in progress.
     * 
     * @return <code>true</code> if AWML file is loading currently and <code>false</code> if not. 
     */
    public function isLoading():Boolean {
    	return loadProgressTracker.isRunning();	
    }
    
    /**
     * Assumes if AWML needs to be initialized (parsed) on load or not.
     * 
     * @param initOnLoad the flag specified if AWML needs to be initialized 
     * (parsed) on load or not.
     */
    public function setInitOnLoad(initOnLoad:Boolean):Void {
        this.initOnLoad = initOnLoad;
    }

    /** 
     * Returns <code>true</code> if current <code>AwmlLoader</code> instance
     * will initialize (parse) AWML on load and <code>false</code> if not.
     * 
     * @return whrever AWML will be initialized on load or not
     */
    public function isInitOnLoad():Boolean {
        return initOnLoad;
    }
    
    /**
     * Sets interval between firing #ON_LOAD_PROGRESS events in milliseconds. If specified interval 
     * is undefined or not a number, default #LOAD_PROGRESS_INTERVAL is used.
     * 
     * @param interval the interval between events.
     */
    public function setLoadProgressInterval(interval:Number):Void {
    	if (interval == null || isNaN(interval)) interval = LOAD_PROGRESS_INTERVAL;
    	
    	if (loadProgressTracker.getDelay() != interval) {
    		loadProgressTracker.setDelay(interval);
    		if (loadProgressTracker.isRunning()) {
    			loadProgressTracker.restart();
    		}
    	}
    }
    
    /**
     * Returns current interval between firing #ON_LOAD_PROGRESS events in milliseconds.
     * 
     * @return interval between events.
     */
    public function getLoadProgressInterval(Void):Number {
    	return loadProgressTracker.getDelay();
    }
    
    /**
     * Fires #ON_LOAD_START event.
     */
    private function fireLoadStartEvent():Void {
        dispatchEvent(createEventObj(ON_LOAD_START));
    }

    /**
     * Fires #ON_LOAD_COMPLETE event.
     */
    private function fireLoadCompleteEvent(awml:XMLNode):Void {
        dispatchEvent(createEventObj(ON_LOAD_COMPLETE, awml));
    }
    
    /**
     * Fires #ON_LOAD_COMPLETE event.
     */
    private function fireInitCompleteEvent():Void {
        dispatchEvent(createEventObj(ON_INIT_COMPLETE));
    }
    
    /**
     * Fires #ON_LOAD_FAIL event.
     */
    private function fireLoadFailEvent():Void {
        dispatchEvent(createEventObj(ON_LOAD_FAIL));
    }

    /**
     * Fires #ON_LOAD_PROGRESS event.
     */
    private function fireLoadProgressEvent():Void {
    	if (loadProgressTracker.isRunning()) {
        	dispatchEvent(createEventObj(ON_LOAD_PROGRESS, loader.getBytesLoaded(), loader.getBytesTotal()));
    	}
    }

    /**
     * Handles <code>LoadVars#onData</code> event.
     */
    private function __onData(awmlStr:String):Void {
    	
    	loadProgressTracker.stop();
    	
        if (awmlStr != null) {
            
            // get awml
            var awml:XMLNode = AwmlUtils.strToXml(awmlStr);
            fireLoadCompleteEvent(awml);
            
            // parse awml
            if (initOnLoad) {
            	AwmlParser.parseAll(awml);
                suspendedInitializer.start();
            } else {
                fireActionEvent(awml);
            }
        } else {
            fireLoadFailEvent();
        }
    }
    
    /**
     * Handles AWML initialization complete event.
     */
    private function __onInit():Void {
        fireInitCompleteEvent();
        fireActionEvent();
    }
}